using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Admin.NET.Application.Entity.Business;
using Admin.NET.Core;
using Admin.NET.Core.Service;
using Furion.DataEncryption;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.EventBus;
using Furion.FriendlyException;
using Lazy.Captcha.Core;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using SKIT.FlurlHttpClient;
using SKIT.FlurlHttpClient.Wechat.Api;
using SKIT.FlurlHttpClient.Wechat.Api.Models;
using UAParser;

namespace Admin.NET.Application.Service.Mp;

/// <summary>
/// 小程序端授权登录
/// </summary>
[ApiDescriptionSettings(new string[] { "业务应用" }, Order = 100)]
public class MpAuthService : IDynamicApiController, ITransient, IPrivateDependency
{
	private readonly SqlSugarRepository<Owner> _rep;

	private readonly UserManager _userManager;

	private readonly SysCacheService _sysCacheService;

	private readonly IHttpContextAccessor _httpContextAccessor;

	private readonly SysOnlineUserService _sysOnlineUserService;

	private readonly IEventPublisher _eventPublisher;

	private readonly SqlSugarRepository<SysWechatUser> _sysWechatUserRep;

	private readonly SysConfigService _sysConfigService;

	private readonly ICaptcha _captcha;

	public MpAuthService(UserManager userManager, SqlSugarRepository<Owner> rep, IHttpContextAccessor httpContextAccessor, SysOnlineUserService sysOnlineUserService, IEventPublisher eventPublisher, ICaptcha captcha, SysCacheService sysCacheService, SqlSugarRepository<SysWechatUser> sysWechatUserRep, SysConfigService sysConfigService)
	{
		_userManager = userManager;
		_rep = rep;
		_sysCacheService = sysCacheService;
		_httpContextAccessor = httpContextAccessor;
		_sysOnlineUserService = sysOnlineUserService;
		_eventPublisher = eventPublisher;
		_sysWechatUserRep = sysWechatUserRep;
		_sysConfigService = sysConfigService;
		_captcha = captcha;
	}

	[HttpPost("/mp/login")]
	[AllowAnonymous]
	[SuppressMonitor]
	public async Task<LoginUserOutput> Login([Required] LoginInput input)
	{
		string keyErrorPasswordCount = "sys_errorPasswordCount:" + input.Account;
		int errorPasswordCount = _sysCacheService.Get<int>(keyErrorPasswordCount);
		if (errorPasswordCount > 5)
		{
			throw Oops.Oh(ErrorCodeEnum.D1027);
		}
		if (await _sysConfigService.GetConfigValue<bool>("sys_captcha") && !_captcha.Validate(input.CodeId.ToString(), input.Code))
		{
			throw Oops.Oh(ErrorCodeEnum.D0008);
		}
		string encryptPasswod = MD5Encryption.Encrypt(input.Password);
		Owner user = await _rep.AsQueryable().Filter(null, isDisabledGobalFilter: true).FirstAsync((Owner u) => u.PhoneNumber.Equals(input.Account) && u.Password.Equals(encryptPasswod));
		if (user == null)
		{
			throw Oops.Oh(ErrorCodeEnum.D1000);
		}
		SysTenant tenant = await _rep.ChangeRepository<SqlSugarRepository<SysTenant>>().GetFirstAsync((SysTenant u) => (long?)u.Id == user.TenantId);
		if (tenant != null)
		{
			if (tenant.Status == StatusEnum.Disable)
			{
				throw Oops.Oh(ErrorCodeEnum.Z1003);
			}
			if (tenant.IsSubscribe && (!tenant.VolumeEnd.HasValue || tenant.VolumeEnd < DateTime.Now.Date || tenant.VolumeStart > DateTime.Now.Date))
			{
				throw Oops.Oh("当前账号不在服务期间，请联系管理员");
			}
			if (user.IsEnable == 0)
			{
				throw Oops.Oh("当前账号不在服务期间，请联系管理员");
			}
		}
		if (!user.Password.Equals(MD5Encryption.Encrypt(input.Password)))
		{
			SysCacheService sysCacheService = _sysCacheService;
			int num = errorPasswordCount + 1;
			sysCacheService.Set(keyErrorPasswordCount, num, TimeSpan.FromMinutes(30.0));
			throw Oops.Oh(ErrorCodeEnum.D1000);
		}
		await _sysOnlineUserService.SingleLogin(user.Id);
		string accessToken = JWTEncryption.Encrypt(expiredTime: await _sysConfigService.GetTokenExpire(), payload: new Dictionary<string, object>
		{
			{ "UserId", user.Id },
			{ "TenantId", user.TenantId },
			{ "Account", user.PhoneNumber },
			{ "RealName", user.Name },
			{
				"AccountType",
				AccountTypeEnum.None
			}
		});
		string refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, await _sysConfigService.GetRefreshTokenExpire());
		_httpContextAccessor.HttpContext.SetTokensOfResponseHeaders(accessToken, refreshToken);
		_httpContextAccessor.HttpContext.Response.Headers["access-token"] = accessToken;
		_httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken;
		_httpContextAccessor.HttpContext.Response.Headers["Access-Control-Expose-Headers"] = "access-token,x-access-token";
		return await GetUserInfo(user);
	}

	/// <summary>
	/// 获取用户信息
	/// </summary>
	/// <returns></returns>
	private async Task<LoginUserOutput> GetUserInfo(Owner user)
	{
		if (user == null)
		{
			throw Oops.Oh(ErrorCodeEnum.D1011);
		}
		_httpContextAccessor.HttpContext.GetRemoteIpAddressToIPv4();
		Parser.GetDefault().Parse(_httpContextAccessor.HttpContext.Request.Headers["User-Agent"]);
		return new LoginUserOutput
		{
			Account = user.PhoneNumber,
			RealName = user.Name,
			Email = user.Email,
			Id = user.Id
		};
	}

	private async Task<WechatApiClient> CreateWechatClient()
	{
		WxPaySub _wechatOptions = await _rep.Context.Queryable<WxPaySub>().FirstAsync();
		if (_wechatOptions == null || string.IsNullOrEmpty(_wechatOptions.SubAppId) || string.IsNullOrEmpty(_wechatOptions.SubAppSecret))
		{
			throw Oops.Oh("微信配置错误");
		}
		WechatApiClient wechatApiClient = new WechatApiClient(new WechatApiClientOptions
		{
			AppId = _wechatOptions.SubAppId,
			AppSecret = _wechatOptions.SubAppSecret
		});
		wechatApiClient.Configure(delegate(CommonClientSettings settings)
		{
			settings.JsonSerializer = new FlurlNewtonsoftJsonSerializer();
		});
		return wechatApiClient;
	}

	[HttpPost("/mp/snsOAuth2")]
	public async Task<string> LoginOAuth2([Required] WechatOAuth2Input input)
	{
		SnsJsCode2SessionResponse resOAuth2 = await WechatApiClientExecuteSnsExtensions.ExecuteSnsJsCode2SessionAsync(request: new SnsJsCode2SessionRequest
		{
			JsCode = input.Code
		}, client: await CreateWechatClient());
		if (resOAuth2.ErrorCode != 0)
		{
			throw Oops.Oh(resOAuth2.ErrorMessage + resOAuth2.ErrorCode);
		}
		return resOAuth2.OpenId;
	}

	[HttpPost("/mp/wxlogin")]
	[AllowAnonymous]
	[SuppressMonitor]
	public async Task<LoginUserOutput> WxLogin([FromQuery] string code)
	{
		WechatApiClient wechat = await CreateWechatClient();
		CgibinTokenRequest cgibinTokenRequest = new CgibinTokenRequest();
		CgibinTokenResponse tokenResponse = await wechat.ExecuteCgibinTokenAsync(cgibinTokenRequest);
		if (!tokenResponse.IsSuccessful())
		{
			throw Oops.Bah(tokenResponse.ErrorMessage);
		}
		WxaBusinessGetUserPhoneNumberRequest request = new WxaBusinessGetUserPhoneNumberRequest
		{
			Code = code,
			AccessToken = tokenResponse.AccessToken
		};
		WxaBusinessGetUserPhoneNumberResponse res = await wechat.ExecuteWxaBusinessGetUserPhoneNumberAsync(request);
		if (!res.IsSuccessful())
		{
			throw Oops.Bah(res.ErrorMessage);
		}
		Owner user = await _rep.AsQueryable().Filter(null, isDisabledGobalFilter: true).FirstAsync((Owner u) => u.PhoneNumber.Equals(res.PhoneInfo.PhoneNumber));
		if (user == null)
		{
			throw Oops.Bah("账号未开通");
		}
		SysTenant tenant = await _rep.ChangeRepository<SqlSugarRepository<SysTenant>>().GetFirstAsync((SysTenant u) => (long?)u.Id == user.TenantId);
		if (tenant.Status == StatusEnum.Disable)
		{
			throw Oops.Oh(ErrorCodeEnum.Z1003);
		}
		if (tenant.IsSubscribe && (!tenant.VolumeEnd.HasValue || tenant.VolumeEnd < DateTime.Now.Date || tenant.VolumeStart > DateTime.Now.Date))
		{
			throw Oops.Oh("当前账号不在服务期间，请联系管理员");
		}
		if (user.IsEnable == 0)
		{
			throw Oops.Oh("当前账号不在服务期间，请联系管理员");
		}
		await _sysOnlineUserService.SingleLogin(user.Id);
		string accessToken = JWTEncryption.Encrypt(expiredTime: await _sysConfigService.GetTokenExpire(), payload: new Dictionary<string, object>
		{
			{ "UserId", user.Id },
			{ "TenantId", user.TenantId },
			{ "Account", user.PhoneNumber },
			{ "RealName", user.Name },
			{
				"AccountType",
				AccountTypeEnum.None
			}
		});
		string refreshToken = JWTEncryption.GenerateRefreshToken(accessToken, await _sysConfigService.GetRefreshTokenExpire());
		_httpContextAccessor.HttpContext.SetTokensOfResponseHeaders(accessToken, refreshToken);
		_httpContextAccessor.HttpContext.Response.Headers["access-token"] = accessToken;
		_httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken;
		_httpContextAccessor.HttpContext.Response.Headers["Access-Control-Expose-Headers"] = "access-token,x-access-token";
		return await GetUserInfo(user);
	}
}
